// baljsn_decoder.h                                                   -*-C++-*-
#ifndef INCLUDED_BALJSN_DECODER
#define INCLUDED_BALJSN_DECODER

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a JSON decoder for 'bdeat' compatible types.
//
//@CLASSES:
//  baljsn::Decoder: JSON decoder for 'bdeat'-compliant types
//
//@SEE_ALSO: baljsn_encoder, baljsn_parserutil, baljsn_parser
//
//@DESCRIPTION: This component provides a class, 'baljsn::Decoder', for
// decoding value-semantic objects in the JSON format.  In particular, the
// 'class' contains a parameterized 'decode' function that decodes an object
// from a specified stream.  There are two overloaded versions of this
// function:
//
//: o one that reads from a 'bsl::streambuf'
//: o one that reads from a 'bsl::istream'
//
// This component can be used with types that support the 'bdeat' framework
// (see the 'bdeat' package for details), which is a compile-time interface for
// manipulating struct-like and union-like objects.  In particular, types
// generated by the 'bas_codegen.pl' tool, and other dynamic types, can be
// decoded using this 'class'.  The 'decode' function can be invoked on any
// object that satisfies the requirements of a sequence, choice, or array
// object as defined in the 'bdlat_sequencefunctions', 'bdlat_choicefunctions',
// and 'bdlat_arrayfunctions' components.
//
// Although the JSON format is easy to read and write and is very useful for
// debugging, it is relatively expensive to encode and decode and relatively
// bulky to transmit.  It is more efficient to use a binary encoding (such as
// BER) if the encoding format is under your control (see 'balber_berdecoder').
//
// Refer to the details of the JSON encoding format supported by this decoder
// in the package documentation file (doc/baljsn.txt).
//
///'validateInputIsUtf8' Option
///----------------------------
// The 'baljsn::DecoderOption' parameter of the 'decode' function has a
// configuration option named 'validateInputIsUtf8'.  If this option is 'true',
// the 'decode' function will succeed only if the encoding of the JSON data is
// UTF-8, which the JSON specification requires.  If the option is 'false',
// 'decode' will not validate that the encoding of the JSON data is UTF-8, and
// may succeed even if the data does not satisfy the UTF-8 validity requirement
// of the JSON specification.  This option primarily affects the acceptance of
// string literals, which are the parts of JSON documents that may have
// rational justification for having non-UTF-8, and therefore invalid, content.
//
// Ideally, users *should* set 'validateInputIsUtf8' to 'true'.  However, some
// legacy applications currently might be trafficking in JSON that contains
// non-UTF-8 with no adverse effects to their clients.  Consequently, this
// option is 'false' by default to maintain backward compatibility.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Decoding into a 'bas_codegen.pl'-generated from data in JSON
///-----------------------------------------------------------------------
// Consider that we want to exchange an employee's information between two
// processes.  To allow this information exchange we will define the XML schema
// representation for that class, use 'bas_codegen.pl' to create the 'Employee'
// 'class' for storing that information, and decode into that object using the
// baljsn decoder.
//
// First, we will define the XML schema inside a file called 'employee.xsd':
//..
//  <?xml version='1.0' encoding='UTF-8'?>
//  <xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'
//             xmlns:test='http://bloomberg.com/schemas/test'
//             targetNamespace='http://bloomberg.com/schemas/test'
//             elementFormDefault='unqualified'>
//
//      <xs:complexType name='Address'>
//          <xs:sequence>
//              <xs:element name='street' type='xs:string'/>
//              <xs:element name='city'   type='xs:string'/>
//              <xs:element name='state'  type='xs:string'/>
//          </xs:sequence>
//      </xs:complexType>
//
//      <xs:complexType name='Employee'>
//          <xs:sequence>
//              <xs:element name='name'        type='xs:string'/>
//              <xs:element name='homeAddress' type='test:Address'/>
//              <xs:element name='age'         type='xs:int'/>
//          </xs:sequence>
//      </xs:complexType>
//
//      <xs:element name='Employee' type='test:Employee'/>
//
//  </xs:schema>
//..
// Then, we will use the 'bas_codegen.pl' tool, to generate the C++ classes for
// this schema.  The following command will generate the header and
// implementation files for the all the classes in the 'test_messages'
// components in the current directory:
//..
//  $ bas_codegen.pl -m msg -p test xsdfile.xsd
//..
// Next, we will create a 'test::Employee' object:
//..
//  test::Employee employee;
//..
// Then, we will create a 'baljsn::Decoder' object:
//..
//  baljsn::Decoder decoder;
//..
// Next, we will specify the input data provided to the decoder:
//..
//  const char INPUT[] = "{\"name\":\"Bob\",\"homeAddress\":{\"street\":"
//                       "\"Lexington Ave\",\"city\":\"New York City\","
//                       "\"state\":\"New York\"},\"age\":21}";
//
//  bsl::istringstream is(INPUT);
//..
// Now, we will decode this object using the 'decode' function of the baljsn
// decoder by providing it a 'baljsn::DecoderOptions' object.  The decoder
// options allow us to specify that unknown elements should *not* be skipped.
// Setting this option to 'false' will result in the decoder returning an error
// on encountering an unknown element:
//..
//  baljsn::DecoderOptions options;
//  options.setSkipUnknownElements(false);
//
//  const int rc = decoder.decode(is, &employee, options);
//  assert(!rc);
//  assert(is);
//..
// Finally, we will verify that the decoded object is as expected:
//..
//  assert("Bob"           == employee.name());
//  assert("Lexington Ave" == employee.homeAddress().street());
//  assert("New York City" == employee.homeAddress().city());
//  assert("New York"      == employee.homeAddress().state());
//  assert(21              == employee.age());
//..

#include <balscm_version.h>

#include <baljsn_decoderoptions.h>
#include <baljsn_parserutil.h>
#include <baljsn_tokenizer.h>

#include <bdlat_attributeinfo.h>
#include <bdlat_choicefunctions.h>
#include <bdlat_customizedtypefunctions.h>
#include <bdlat_enumfunctions.h>
#include <bdlat_formattingmode.h>
#include <bdlat_selectioninfo.h>
#include <bdlat_sequencefunctions.h>
#include <bdlat_typecategory.h>
#include <bdlat_valuetypefunctions.h>

#include <bdlb_printmethods.h>

#include <bdlma_localsequentialallocator.h>

#include <bslmf_assert.h>

#include <bsls_assert.h>
#include <bsls_types.h>

#include <bsl_iostream.h>
#include <bsl_sstream.h>
#include <bsl_streambuf.h>
#include <bsl_string.h>

namespace BloombergLP {
namespace baljsn {

                               // =============
                               // class Decoder
                               // =============

class Decoder {
    // This class provides a mechanism for decoding JSON data into
    // value-semantic objects.  The 'decode' methods are function templates
    // that will decode any object that meets the requirements of a sequence,
    // choice, or array object as defined in the 'bdlat_sequencefunctions',
    // 'bdlat_choicefunctions', and 'bdlat_choicefunctions' components
    // respectively.  These generic frameworks provide a common compile-time
    // interface for manipulating struct-like and union-like objects.  In
    // particular, the types generated by 'bas_codegen.pl' provide the
    // necessary interface and can be decoded using this component.

    // DATA
    bsl::ostringstream  d_logStream;            // stream to record errors
    Tokenizer           d_tokenizer;            // JSON tokenizer
    bsl::string         d_elementName;          // current element name
    int                 d_currentDepth;         // current decoding depth
    int                 d_maxDepth;             // max decoding depth
    bool                d_skipUnknownElements;  // skip unknown elements flag

    // FRIENDS
    friend struct Decoder_DecodeImpProxy;
    friend struct Decoder_ElementVisitor;

    // PRIVATE MANIPULATORS
    template <class TYPE>
    int decodeImp(TYPE *value, int mode, bdlat_TypeCategory::DynamicType);
    template <class TYPE>
    int decodeImp(TYPE *value, int mode, bdlat_TypeCategory::Sequence);
    template <class TYPE>
    int decodeImp(TYPE *value, int mode, bdlat_TypeCategory::Choice);
    template <class TYPE>
    int decodeImp(TYPE *value, int mode, bdlat_TypeCategory::Enumeration);
    template <class TYPE>
    int decodeImp(TYPE *value, int mode, bdlat_TypeCategory::CustomizedType);
    template <class TYPE>
    int decodeImp(TYPE *value, int mode, bdlat_TypeCategory::Simple);
    template <class TYPE>
    int decodeImp(TYPE *value, int mode, bdlat_TypeCategory::Array);
    template <class TYPE>
    int decodeImp(TYPE *value, int mode, bdlat_TypeCategory::NullableValue);
    int decodeImp(bsl::vector<char>         *value,
                  int                        mode,
                  bdlat_TypeCategory::Array);
    template <class TYPE, class ANY_CATEGORY>
    int decodeImp(TYPE *value, ANY_CATEGORY category);
        // Decode into the specified 'value', of a (template parameter) 'TYPE'
        // corresponding to the specified 'bdeat' 'category', the JSON data
        // currently referred to by the tokenizer owned by this object, using
        // the specified formatting 'mode'.  Return 0 on success and a non-zero
        // value otherwise.  The behavior is undefined unless 'value'
        // corresponds to the specified 'bdeat' category and 'mode' is a valid
        // formatting mode as specified in 'bdlat_FormattingMode'.  Note that
        // 'ANY_CATEGORY' shall be a tag-type defined in 'bdlat_TypeCategory'.

    bsl::ostream& logTokenizerError(const char *alternateString);
        // Log the latest tokenizer error to 'd_logStream'.  If the tokenizer
        // did not have an error, log the specified 'alternateString'.  Return
        // a reference to 'd_logStream'.

    int skipUnknownElement(const bslstl::StringRef& elementName);
        // Skip the unknown element specified by 'elementName' by discarding
        // all the data associated with it and advancing the parser to the next
        // element.  Return 0 on success and a non-zero value otherwise.

  private:
    // NOT IMPLEMENTED
    Decoder(const Decoder&);
    Decoder& operator=(const Decoder&);

  public:
    // CREATORS
    explicit Decoder(bslma::Allocator *basicAllocator = 0);
        // Construct a decoder object using the optionally specified
        // 'basicAllocator'.  If 'basicAllocator' is 0, the default allocator
        // is used.

    // MANIPULATORS
    template <class TYPE>
    int decode(bsl::streambuf        *streamBuf,
               TYPE                  *value,
               const DecoderOptions&  options);
    template <class TYPE>
    int decode(bsl::streambuf        *streamBuf,
               TYPE                  *value,
               const DecoderOptions  *options);
        // Decode into the specified 'value', of a (template parameter) 'TYPE',
        // the JSON data read from the specified 'streamBuf' and using the
        // specified 'options'.  Specifying a nullptr 'options' is equivalent
        // to passing a default-constructed DecoderOptions in 'options'.
        // 'TYPE' shall be a 'bdeat'-compatible sequence, choice, or array
        // type, or a 'bdeat'-compatible dynamic type referring to one of those
        // types.  Return 0 on success, and a non-zero value otherwise.  Note
        // that this operation internally buffers input from 'streambuf', and
        // if decoding is successful, will attempt to update the input position
        // of 'streambuf' to the last unprocessed byte.

    template <class TYPE>
    int decode(bsl::istream&          stream,
               TYPE                  *value,
               const DecoderOptions&  options);
    template <class TYPE>
    int decode(bsl::istream&          stream,
               TYPE                  *value,
               const DecoderOptions  *options);
        // Decode into the specified 'value', of a (template parameter) 'TYPE',
        // the JSON data read from the specified 'stream' and using the
        // specified 'options'.  'TYPE' shall be a 'bdeat'-compatible sequence,
        // choice, or array type, or a 'bdeat'-compatible dynamic type
        // referring to one of those types.  Specifying a nullptr 'options' is
        // equivalent to passing a default-constructed DecoderOptions in
        // 'options'.  Return 0 on success, and a non-zero value otherwise.
        // Note that this operation internally buffers input from 'stream', and
        // if decoding is successful, will attempt to update the input position
        // of 'stream' to the last unprocessed byte.

    template <class TYPE>
    int decode(bsl::streambuf *streamBuf, TYPE *value);
        // Decode an object of (template parameter) 'TYPE' from the specified
        // 'streamBuf' and load the result into the specified modifiable
        // 'value'.  Return 0 on success, and a non-zero value otherwise.
        //
        // DEPRECATED: Use the 'decode' function passed a reference to a
        // non-modifiable 'DecoderOptions' object instead.

    template <class TYPE>
    int decode(bsl::istream& stream, TYPE *value);
        // Decode an object of (template parameter) 'TYPE' from the specified
        // 'stream' and load the result into the specified modifiable 'value'.
        // Return 0 on success, and a non-zero value otherwise.  Note that
        // 'stream' will be invalidated if the decoding fails.
        //
        // DEPRECATED: Use the 'decode' function passed a reference to a
        // non-modifiable 'DecoderOptions' object instead.

    // ACCESSORS
    bsl::string loggedMessages() const;
        // Return a string containing any error, warning, or trace messages
        // that were logged during the last call to the 'decode' method.  The
        // log is reset each time 'decode' is called.
};

                       // =============================
                       // struct Decoder_ElementVisitor
                       // =============================

struct Decoder_ElementVisitor {
    // This 'class' implements a visitor for decoding elements within a
    // sequence, choice, or array type.  This is a component-private class and
    // should not be used outside of this component.  Note that the operators
    // provided in this 'class' match the function signatures required of
    // visitors decoding into elements of compatible types.

    // DATA
    Decoder *d_decoder_p;  // decoder (held, not owned)
    int      d_mode;       // formatting mode

    // CREATORS

    // Creators have been omitted to allow simple static initialization of this
    // struct.

    // MANIPULATORS
    template <class TYPE>
    int operator()(TYPE *value);
        // Decode into the specified 'value' the data in the JSON format.
        // Return 0 on success and a non-zero value otherwise.

    template <class TYPE, class INFO>
    int operator()(TYPE *value, const INFO& info);
        // Decode into the specified 'value' using the specified 'info' the
        // data in the JSON format.  Return 0 on success and a non-zero value
        // otherwise.
};

                       // =============================
                       // struct Decoder_DecodeImpProxy
                       // =============================

struct Decoder_DecodeImpProxy {
    // This class provides a functor that dispatches the appropriate
    // 'decodeImp' method for a 'bdeat' Dynamic type.  Note that the operators
    // provided in this 'class' match the function signatures required of
    // visitors decoding into compatible types.

    // DATA
    Decoder *d_decoder_p;  // decoder (held, not owned)
    int             d_mode;       // formatting mode

    // CREATORS

    // Creators have been omitted to allow simple static initialization of this
    // struct.

    // MANIPULATORS
    template <class TYPE>
    int operator()(TYPE *, bslmf::Nil);

    template <class TYPE, class ANY_CATEGORY>
    int operator()(TYPE *object, ANY_CATEGORY category);
        // Dencode into the specified 'value' of the specified 'bdeat'
        // 'category' from the data in the JSON format.  Return 0 on success
        // and a non-zero value otherwise.
};

// ============================================================================
//                            INLINE DEFINITIONS
// ============================================================================

                               // -------------
                               // class Decoder
                               // -------------

// PRIVATE MANIPULATORS
template <class TYPE>
inline
int Decoder::decodeImp(TYPE *value, int mode, bdlat_TypeCategory::DynamicType)
{
    Decoder_DecodeImpProxy proxy = { this, mode };
    return bdlat_TypeCategoryUtil::manipulateByCategory(value, proxy);
}

template <class TYPE>
int Decoder::decodeImp(TYPE *value, int mode, bdlat_TypeCategory::Sequence)
{
    if (bdlat_FormattingMode::e_UNTAGGED & mode) {
        // This is an anonymous element.  Do not read anything and instead
        // decode into the corresponding sub-element.

        if (bdlat_SequenceFunctions::hasAttribute(
                                   *value,
                                   d_elementName.data(),
                                   static_cast<int>(d_elementName.length()))) {
            Decoder_ElementVisitor visitor = { this, mode };

            if (0 != bdlat_SequenceFunctions::manipulateAttribute(
                                   value,
                                   visitor,
                                   d_elementName.data(),
                                   static_cast<int>(d_elementName.length()))) {
                d_logStream << "Could not decode sequence, error decoding "
                            << "element or bad element name '"
                            << d_elementName << "' \n";
                return -1;                                            // RETURN
            }
        }
        else {
            if (d_skipUnknownElements) {
                const int rc = skipUnknownElement(d_elementName);
                if (rc) {
                    d_logStream << "Error reading unknown element '"
                                << d_elementName << "' or after it\n";
                    return -1;                                        // RETURN
                }
            }
            else {
                d_logStream << "Unknown element '" << d_elementName
                            << "' found\n";
                return -1;                                            // RETURN
            }
        }
    }
    else {
        if (++d_currentDepth > d_maxDepth) {
            d_logStream << "Maximum allowed decoding depth reached: "
                        << d_currentDepth << "\n";
            return -1;                                                // RETURN
        }

        if (Tokenizer::e_START_OBJECT != d_tokenizer.tokenType()) {
            d_logStream << "Could not decode sequence, missing starting '{'\n";
            return -1;                                                // RETURN
        }

        int rc = d_tokenizer.advanceToNextToken();
        if (rc) {
            d_logStream << "Could not decode sequence, ";
            logTokenizerError("error") << " reading token after '{'\n";
            return -1;                                                // RETURN
        }

        while (Tokenizer::e_ELEMENT_NAME == d_tokenizer.tokenType()) {
            bslstl::StringRef elementName;
            rc = d_tokenizer.value(&elementName);
            if (rc) {
                d_logStream << "Error reading attribute name after '{'\n";
                return -1;                                            // RETURN
            }

            if (bdlat_SequenceFunctions::hasAttribute(
                                     *value,
                                     elementName.data(),
                                     static_cast<int>(elementName.length()))) {
                d_elementName = elementName;

                rc = d_tokenizer.advanceToNextToken();
                if (rc) {
                    logTokenizerError("Error") << " reading value for"
                                 << " attribute '" << d_elementName << "' \n";
                    return -1;                                        // RETURN
                }

                Decoder_ElementVisitor visitor = { this, mode };

                if (0 != bdlat_SequenceFunctions::manipulateAttribute(
                                   value,
                                   visitor,
                                   d_elementName.data(),
                                   static_cast<int>(d_elementName.length()))) {
                    d_logStream << "Could not decode sequence, error decoding "
                                << "element or bad element name '"
                                << d_elementName << "' \n";
                    return -1;                                        // RETURN
                }
            }
            else {
                if (d_skipUnknownElements) {
                    rc = skipUnknownElement(elementName);
                    if (rc) {
                        d_logStream << "Error reading unknown element '"
                                    << elementName << "' or after it\n";
                        return -1;                                    // RETURN
                    }
                }
                else {
                    d_logStream << "Unknown element '"
                                << elementName << "' found\n";
                    return -1;                                        // RETURN
                }
            }

            rc = d_tokenizer.advanceToNextToken();
            if (rc) {
                d_logStream << "Could not decode sequence, ";
                logTokenizerError("error") << " reading token"
                            << " after value for attribute '"
                            << d_elementName << "' \n";
                return -1;                                            // RETURN
            }
        }

        if (Tokenizer::e_END_OBJECT != d_tokenizer.tokenType()) {
            d_logStream << "Could not decode sequence, "
                        << "missing terminator '}' or seperator ','\n";
            return -1;                                                // RETURN
        }

        --d_currentDepth;
    }
    return 0;
}

template <class TYPE>
int Decoder::decodeImp(TYPE                       *value,
                       int                         mode,
                       bdlat_TypeCategory::Choice)
{
    if (bdlat_FormattingMode::e_UNTAGGED & mode) {
        // This is an anonymous element.  Do not read anything and instead
        // decode into the corresponding sub-element.

        bslstl::StringRef selectionName;
        selectionName.assign(d_elementName.begin(), d_elementName.end());

        if (bdlat_ChoiceFunctions::hasSelection(
                                   *value,
                                   selectionName.data(),
                                   static_cast<int>(selectionName.length()))) {
            if (0 != bdlat_ChoiceFunctions::makeSelection(
                                   value,
                                   selectionName.data(),
                                   static_cast<int>(selectionName.length()))) {
                d_logStream << "Could not decode choice, bad selection name '"
                            << selectionName << "' \n";
                return -1;                                            // RETURN
            }

            Decoder_ElementVisitor visitor = { this, mode };

            if (0 != bdlat_ChoiceFunctions::manipulateSelection(value,
                                                                visitor)) {
                d_logStream << "Could not decode choice, selection "
                            << "was not decoded\n";
                return -1;                                            // RETURN
            }
        }
        else {
            if (d_skipUnknownElements) {
                const int rc = skipUnknownElement(selectionName);
                if (rc) {
                    d_logStream << "Error reading unknown element '"
                                << selectionName << "' or after that "
                                << "element\n";
                    return -1;                                        // RETURN
                }
            }
            else {
                d_logStream << "Unknown element '"
                            << selectionName << "' found\n";
                return -1;                                            // RETURN
            }
        }
    }
    else {
        if (++d_currentDepth > d_maxDepth) {
            d_logStream << "Maximum allowed decoding depth reached: "
                        << d_currentDepth << "\n";
            return -1;                                                // RETURN
        }

        if (Tokenizer::e_START_OBJECT != d_tokenizer.tokenType()) {
            d_logStream << "Could not decode choice, missing starting {\n";
            return -1;                                                // RETURN
        }

        int rc = d_tokenizer.advanceToNextToken();
        if (rc) {
            d_logStream << "Could not decode choice, ";
            logTokenizerError("error") << " reading token after {\n";
            return -1;                                                // RETURN
        }

        if (Tokenizer::e_ELEMENT_NAME == d_tokenizer.tokenType()) {
            bslstl::StringRef selectionName;
            rc = d_tokenizer.value(&selectionName);
            if (rc) {
                d_logStream << "Error reading selection name after '{'\n";
                return -1;                                            // RETURN
            }

            if (bdlat_ChoiceFunctions::hasSelection(
                                   *value,
                                   selectionName.data(),
                                   static_cast<int>(selectionName.length()))) {
                if (0 != bdlat_ChoiceFunctions::makeSelection(
                                   value,
                                   selectionName.data(),
                                   static_cast<int>(selectionName.length()))) {
                    d_logStream << "Could not decode choice, bad selection "
                                << "name '" << selectionName << "' \n";
                    return -1;                                        // RETURN
                }

                rc = d_tokenizer.advanceToNextToken();
                if (rc) {
                    d_logStream << "Could not decode choice, ";
                    logTokenizerError("error") << " reading value \n";
                    return -1;                                        // RETURN
                }

                Decoder_ElementVisitor visitor = { this, mode };

                if (0 != bdlat_ChoiceFunctions::manipulateSelection(value,
                                                                    visitor)) {
                    d_logStream << "Could not decode choice, selection "
                                << "was not decoded\n";
                    return -1;                                        // RETURN
                }
            }
            else {
                if (d_skipUnknownElements) {
                    rc = skipUnknownElement(selectionName);
                    if (rc) {
                        d_logStream << "Error reading unknown element '"
                                    << selectionName << "' or after that "
                                    << "element\n";
                        return -1;                                    // RETURN
                    }
                }
                else {
                    d_logStream << "Unknown element '"
                                << selectionName << "' found\n";
                    return -1;                                        // RETURN
                }
            }

            rc = d_tokenizer.advanceToNextToken();
            if (rc) {
                d_logStream << "Could not decode choice, ";
                logTokenizerError("error") << " reading token after value for"
                                                               " selection \n";

                return -1;                                            // RETURN
            }
        }

        if (Tokenizer::e_END_OBJECT != d_tokenizer.tokenType()) {
            d_logStream << "Could not decode choice, "
                        << "missing terminator '}'\n";
            return -1;                                                // RETURN
        }

        --d_currentDepth;
    }
    return 0;
}

template <class TYPE>
int Decoder::decodeImp(TYPE *value,
                              int,
                              bdlat_TypeCategory::Enumeration)
{
    enum { k_MIN_ENUM_STRING_LENGTH = 2 };

    if (Tokenizer::e_ELEMENT_VALUE != d_tokenizer.tokenType()) {
        d_logStream << "Enumeration element value was not found\n";
        return -1;                                                    // RETURN
    }

    bslstl::StringRef dataValue;
    int rc = d_tokenizer.value(&dataValue);
    if (rc
     || dataValue.length() <= k_MIN_ENUM_STRING_LENGTH
     || '"'                != dataValue[0]
     || '"'                != dataValue[dataValue.length() - 1]) {
        d_logStream << "Error reading enumeration value\n";
        return -1;                                                    // RETURN
    }

    // This used to be 'BUF_SIZE' but that caused a #define conflict.

    const int                                     BAL_BUF_SIZE = 128;
    bdlma::LocalSequentialAllocator<BAL_BUF_SIZE> bufferAllocator;
    bsl::string                                   tmpString(&bufferAllocator);

    rc = baljsn::ParserUtil::getValue(&tmpString, dataValue);
    if (rc) {
        d_logStream << "Error reading enumeration value\n";
        return -1;                                                    // RETURN
    }

    rc = bdlat_EnumFunctions::fromString(value,
                                         tmpString.data(),
                                         static_cast<int>(tmpString.size()));

    if (rc) {
        d_logStream << "Could not decode Enum String, value not allowed \""
                    << dataValue << "\"\n";
    }
    return rc;
}

template <class TYPE>
int Decoder::decodeImp(TYPE *value,
                              int,
                              bdlat_TypeCategory::CustomizedType)
{
    if (Tokenizer::e_ELEMENT_VALUE != d_tokenizer.tokenType()) {
        d_logStream << "Customized element value was not found\n";
        return -1;                                                    // RETURN
    }

    bslstl::StringRef dataValue;
    int rc = d_tokenizer.value(&dataValue);
    if (rc) {
        d_logStream << "Error reading customized type value\n";
        return -1;                                                    // RETURN
    }

    typename bdlat_CustomizedTypeFunctions::BaseType<TYPE>::Type valueBaseType;

    rc = ParserUtil::getValue(&valueBaseType, dataValue);
    if (rc) {
        d_logStream << "Could not decode Enum Customized, "
                    << "value not allowed \"" << dataValue << "\"\n";
        return -1;                                                    // RETURN
    }

    rc = bdlat_CustomizedTypeFunctions::convertFromBaseType(value,
                                                            valueBaseType);
    if (rc) {
        d_logStream << "Could not convert base type to customized type, "
                    << "base value disallowed: \"";
        bdlb::PrintMethods::print(d_logStream, valueBaseType, 0, -1);
        d_logStream << "\"\n";
    }
    return rc;
}

template <class TYPE>
int Decoder::decodeImp(TYPE *value,
                              int,
                              bdlat_TypeCategory::Simple)
{
    if (Tokenizer::e_ELEMENT_VALUE != d_tokenizer.tokenType()) {
        d_logStream << "Simple element value was not found\n";
        return -1;                                                    // RETURN
    }

    bslstl::StringRef dataValue;
    int rc = d_tokenizer.value(&dataValue);
    if (rc) {
        d_logStream << "Error reading simple value\n";
        return -1;                                                    // RETURN
    }

    return ParserUtil::getValue(value, dataValue);
}

inline
int Decoder::decodeImp(bsl::vector<char> *value,
                              int,
                              bdlat_TypeCategory::Array)
{
    if (Tokenizer::e_ELEMENT_VALUE != d_tokenizer.tokenType()) {
        d_logStream << "Could not decode vector<char> "
                    << "expected as an element value\n";
        return -1;                                                    // RETURN
    }

    bslstl::StringRef dataValue;
    int rc = d_tokenizer.value(&dataValue);

    if (rc) {
        d_logStream << "Error reading customized type element value\n";
        return -1;                                                    // RETURN
    }

    return ParserUtil::getValue(value, dataValue);
}

template <class TYPE>
int Decoder::decodeImp(TYPE                      *value,
                       int                        mode,
                       bdlat_TypeCategory::Array)
{
    if (Tokenizer::e_START_ARRAY != d_tokenizer.tokenType()) {
        d_logStream << "Could not decode vector, missing start token: '['\n";
        return -1;                                                    // RETURN
    }

    int rc = d_tokenizer.advanceToNextToken();
    if (rc) {
        logTokenizerError("Error") << " reading array.\n";
        return rc;                                                    // RETURN
    }

    int i = 0;
    while (Tokenizer::e_END_ARRAY != d_tokenizer.tokenType()) {
        if (Tokenizer::e_ELEMENT_VALUE == d_tokenizer.tokenType()
         || Tokenizer::e_START_OBJECT  ==
                                                     d_tokenizer.tokenType()) {
            ++i;
            bdlat_ArrayFunctions::resize(value, i);

            Decoder_ElementVisitor visitor = { this, mode };

            if (0 != bdlat_ArrayFunctions::manipulateElement(value,
                                                             visitor,
                                                             i - 1)) {
                d_logStream << "Error adding element '" << i - 1 << "'\n";
                return -1;                                            // RETURN
            }

            rc = d_tokenizer.advanceToNextToken();
            if (rc) {
                logTokenizerError("Error") << " reading token after value of"
                                " element '" << i - 1 << "'\n";
                return rc;                                            // RETURN
            }
        }
        else {
            d_logStream << "Erroneous token found instead of array element\n";
            return -1;                                                // RETURN
        }
    }

    if (Tokenizer::e_END_ARRAY != d_tokenizer.tokenType()) {
        d_logStream << "Could not decode vector, missing end token: ']'\n";
        return -1;                                                    // RETURN
    }

    return 0;
}

template <class TYPE>
int Decoder::decodeImp(TYPE                              *value,
                       int                                mode,
                       bdlat_TypeCategory::NullableValue)
{
    enum { k_NULL_VALUE_LENGTH = 4 };

    if (Tokenizer::e_ELEMENT_VALUE == d_tokenizer.tokenType()) {
        bslstl::StringRef dataValue;
        const int rc = d_tokenizer.value(&dataValue);
        if (rc) {
            return rc;                                                // RETURN
        }

        if (k_NULL_VALUE_LENGTH == dataValue.length()
         && 'n'                      == dataValue[0]
         && 'u'                      == dataValue[1]
         && 'l'                      == dataValue[2]
         && 'l'                      == dataValue[3]) {
            return 0;                                                 // RETURN
        }
    }

    bdlat_NullableValueFunctions::makeValue(value);

    Decoder_ElementVisitor visitor = { this, mode };
    return bdlat_NullableValueFunctions::manipulateValue(value, visitor);
}

template <class TYPE, class ANY_CATEGORY>
inline
int Decoder::decodeImp(TYPE *, ANY_CATEGORY)
{
    BSLS_ASSERT_OPT(!"Unreachable");

    return -1;
}

// CREATORS
inline
Decoder::Decoder(bslma::Allocator *basicAllocator)
: d_logStream(basicAllocator)
, d_tokenizer(basicAllocator)
, d_elementName(basicAllocator)
, d_currentDepth(0)
, d_maxDepth(0)
, d_skipUnknownElements(false)
{
}

// MANIPULATORS
template <class TYPE>
int Decoder::decode(bsl::streambuf        *streamBuf,
                    TYPE                  *value,
                    const DecoderOptions&  options)
{
    BSLS_ASSERT(streamBuf);
    BSLS_ASSERT(value);

    d_logStream.clear();
    d_logStream.str("");

    bdlat_TypeCategory::Value category =
                                bdlat_TypeCategoryFunctions::select(*value);

    if (bdlat_TypeCategory::e_SEQUENCE_CATEGORY != category
     && bdlat_TypeCategory::e_CHOICE_CATEGORY   != category
     && bdlat_TypeCategory::e_ARRAY_CATEGORY    != category) {
        d_logStream << "The object being decoded must be a Sequence, "
                    << "Choice, or Array type\n";
        return -1;                                                    // RETURN
    }

    d_tokenizer.reset(streamBuf);
    d_tokenizer.setAllowStandAloneValues(false);
    d_tokenizer.setAllowHeterogenousArrays(false);
    d_tokenizer.setAllowNonUtf8StringLiterals(!options.validateInputIsUtf8());

    typedef typename bdlat_TypeCategory::Select<TYPE>::Type TypeCategory;

    int rc = d_tokenizer.advanceToNextToken();
    if (rc) {
        logTokenizerError("Error") << " advancing to the first token. "
                             "Expecting a '{' or '[' as the first character\n";
        return rc;                                                    // RETURN
    }

    bdlat_ValueTypeFunctions::reset(value);

    d_maxDepth            = options.maxDepth();
    d_skipUnknownElements = options.skipUnknownElements();

    rc = decodeImp(value, 0, TypeCategory());

    d_tokenizer.resetStreamBufGetPointer();

    return rc;
}

template <class TYPE>
int Decoder::decode(bsl::streambuf        *streamBuf,
                    TYPE                  *value,
                    const DecoderOptions  *options)
{
    DecoderOptions localOpts;
    return decode(streamBuf, value, options ? *options : localOpts);
}

template <class TYPE>
int Decoder::decode(bsl::istream&          stream,
                    TYPE                  *value,
                    const DecoderOptions&  options)
{
    if (!stream.good()) {
        return -1;                                                    // RETURN
    }

    if (0 != decode(stream.rdbuf(), value, options)) {
        stream.setstate(bsl::ios_base::failbit);
        return -1;                                                    // RETURN
    }

    return 0;
}

template <class TYPE>
int Decoder::decode(bsl::istream&          stream,
                    TYPE                  *value,
                    const DecoderOptions  *options)
{
    DecoderOptions localOpts;
    return decode(stream, value, options ? *options : localOpts);
}

template <class TYPE>
int Decoder::decode(bsl::streambuf *streamBuf, TYPE *value)
{
    const DecoderOptions options;
    return decode(streamBuf, value, options);
}

template <class TYPE>
int Decoder::decode(bsl::istream& stream, TYPE *value)
{
    const DecoderOptions options;
    return decode(stream, value, options);
}

// ACCESSORS
inline
bsl::string Decoder::loggedMessages() const
{
    return d_logStream.str();
}

                       // -----------------------------
                       // struct Decoder_ElementVisitor
                       // -----------------------------

template <class TYPE>
inline
int Decoder_ElementVisitor::operator()(TYPE *value)
{
    typedef typename bdlat_TypeCategory::Select<TYPE>::Type TypeCategory;
    return d_decoder_p->decodeImp(value, d_mode, TypeCategory());
}

template <class TYPE, class INFO>
inline
int Decoder_ElementVisitor::operator()(TYPE *value, const INFO& info)
{
    typedef typename bdlat_TypeCategory::Select<TYPE>::Type TypeCategory;
    return d_decoder_p->decodeImp(value,
                                  info.formattingMode(),
                                  TypeCategory());
}

                       // -----------------------------
                       // struct Decoder_DecodeImpProxy
                       // -----------------------------

// MANIPULATORS
template <class TYPE>
inline
int Decoder_DecodeImpProxy::operator()(TYPE *, bslmf::Nil)
{
    BSLS_ASSERT_OPT(!"Unreachable");

    return -1;
}

template <class TYPE, class ANY_CATEGORY>
inline
int Decoder_DecodeImpProxy::operator()(TYPE         *object,
                                       ANY_CATEGORY  category)
{
    return d_decoder_p->decodeImp(object, d_mode, category);
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
